#include "54func.h"
#include "serverFunc.h"
#include "assistFuncServer.h"
#include "log.h"

#define FILELIMIT 100000000
#define SHA1LEN 40

// 下载文件,serverPuts函数
// 非mmap版本transFile
static int transFile(int netfd, int filefd)
{
    train_t train;
    if(recvMsg(netfd,&train) == 0){
        // 'e'处理报错
        return -1;
    }
    // 'o'正常操作
    // 先收lseek长度
    recvn(netfd,&train.length,sizeof(train.length));
    recvn(netfd,train.data,train.length);
    // 断点续传lseek
    off_t offset;
    memcpy(&offset,train.data+1,train.length-1);
    lseek(filefd,offset,SEEK_SET);
    // 发送文件内容
    while(1){
        ssize_t sret = read(filefd,train.data,TRAINLEN);
        train.length = sret;
        ssize_t sret1 = send(netfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
        if(sret == 0 || sret1 == -1){
            // 发完空包或者触发SIGPIPE退出
            break;
        }
    }
    close(filefd);
    return 0;
}

// 发送文件,char获取cwd,cmd_t获取指令及参数
int serverPuts(MYSQL *conn, int netfd, const cmd_t *pcmd)
{
    // 1 查表
    char sql[4096];
    memset(sql,0,sizeof(sql));
    sprintf(sql,"select sha1 from filesystem where "
            "user = '%s' and path = '%s' and tomb = 0;"
            ,pcmd->user,pcmd->argv1);
    mysql_query(conn,sql);
    SQL_LOG(conn);
    MYSQL_RES *res;
    res = mysql_store_result(conn);
    if(mysql_num_rows(res) <= 0){
        // 1.1 "e"+msg不存在就报错
        errorSend(netfd,NULL);
        errorSend(netfd,"文件不存在!");
    }
    else{
        // 1.2 "o"存在则获取sha1Num
        successSend(netfd);
        char sha1Num[SHA1LEN+1] = {0};
        MYSQL_ROW row;
        row = mysql_fetch_row(res);
        memcpy(sha1Num,row[0],SHA1LEN);
        // 获取文件名
        char fileName[PATH_MAX] = {0};
        sprintf(fileName,"%s%s",rootDir,sha1Num);
        // 获取文件大小
        int filefd = open(fileName,O_RDWR);
        ERROR_CHECK(filefd,-1,"open in serverPuts");
        printf("filefd = %d\n",filefd);
        struct stat statbuf;
        fstat(filefd,&statbuf);
        // 发送文件长度
        train_t train;
        train.length = sizeof(statbuf.st_size);
        memcpy(train.data,&statbuf.st_size,train.length);
        send(netfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
        // 非mmap发送文件
        transFile(netfd,filefd);
    }
    mysql_free_result(res);
    return 0;
}



// 上传文件serverGets函数
// Client非mmap时recvFile
static int recvFile(int netfd, const char* fileName)
{
    train_t train;
    // 文件已存在就续传，不存在则新建文件
    int filefd = open(fileName,O_RDWR|O_CREAT,0666);
    ERROR_CHECK(filefd,-1,"open in recvFile");
    printf("filefd = %d\n",filefd);
    // 告知Client正常执行以及需要lseek的长度
    successSend(netfd);
    off_t offset = lseek(filefd,0,SEEK_END);
    ERROR_CHECK(offset,-1,"lseek in recvFile");
    train.length = sizeof(offset);
    memcpy(train.data,&offset,train.length);
    send(netfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    // 接收文件内容
    while(1){
        // recv
        recvn(netfd,&train.length,sizeof(train.length));
        if(train.length == 0){  // 读完会读到空包就退出循环
            break;
        }
        recvn(netfd,train.data,train.length);
        // write
        ssize_t sret = write(filefd,train.data,train.length);
        ERROR_CHECK(sret,-1,"write in recvFile");
    }
    close(filefd);
    return 0;
}
// Client用mmap时recvFile
static int recvFileMmap(int netfd, const char* fileName, off_t fileSize)
{
    train_t train;
    // 文件已存在就续传，不存在则新建文件
    int filefd = open(fileName,O_RDWR|O_CREAT,0666);
    ERROR_CHECK(filefd,-1,"open in recvFile");
    printf("filefd = %d\n",filefd);
    // 告知Client正常执行以及需要lseek的长度
    successSend(netfd);
    off_t offset = lseek(filefd,0,SEEK_END);
    ERROR_CHECK(offset,-1,"lseek in recvFile");
    train.length = sizeof(offset);
    memcpy(train.data,&offset,train.length);
    send(netfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    // 接收文件内容
    fileSize -= offset;
    while(1){
        int curSize = 0;
        if(fileSize >= TRAINLEN){
            curSize = TRAINLEN;
        }
        else{
            curSize = fileSize;
        }
        recvn(netfd,train.data,curSize);
        // write
        ssize_t sret = write(filefd,train.data,curSize);
        ERROR_CHECK(sret,-1,"write in recvFileMmap");
        fileSize -= curSize;
        if(fileSize == 0){
            //接收文件完毕
            break;
        }
    }
    close(filefd);
    return 0;
}

// 接收文件,char获取cwd,cmd_t获取指令及参数,包含断点续传
int serverGets(MYSQL *conn, int netfd, cmd_t *pcmd)
{
    // 1 先收sha1值
    char sha1Num[SHA1LEN+1] = {0};
    recvn(netfd,sha1Num,SHA1LEN);
    // 2 查询sha1值
    char sql[4096];
    memset(sql,0,sizeof(sql));
    sprintf(sql,"select * from filesystem where sha1 = '%s';",sha1Num);
    mysql_query(conn,sql);
    SQL_LOG(conn);
    MYSQL_RES *res;
    res = mysql_store_result(conn);
    if(mysql_num_rows(res) == 0) {
        // 2.1 发"o"+"e"+msg未找到文件,普通上传,调用函数
        successSend(netfd);
        errorSend(netfd,NULL);
        errorSend(netfd,"普通上传!");
        // 接收文件大小
        train_t train;
        off_t fileSize;
        recvn(netfd,&train.length,sizeof(train.length));
        recvn(netfd,train.data,train.length);
        memcpy(&fileSize,train.data,train.length);
        // 根据sha1Num获取真实文件路径
        char realName[PATH_MAX] = {0};
        sprintf(realName,"%s%s",rootDir,sha1Num);
        // 根据大小选择适合的接收版本
        if(fileSize > FILELIMIT){
            recvFileMmap(netfd,realName,fileSize);
        }
        else{
            recvFile(netfd,realName);
        }
        // 插表
        insertFileTable(conn,pcmd,sha1Num);
        SQL_LOG(conn);
    }
    else if(mysql_num_rows(res) > 0){
        // 判断路径是否正确
        if(!pathJudge(conn,pcmd->argv2,pcmd->user,"d")){
            errorSend(netfd,NULL);
            errorSend(netfd,"目录路径不正确!");
            return -1;
        }
        // path+filename = filePath
        concatFileName(pcmd->argv1,pcmd->argv2);
        // 查表
        bzero(sql,sizeof(sql));
        sprintf(sql,"select * from filesystem where "
                "user = '%s' and path = '%s' and sha1 = '%s' and tomb = 0;",
                pcmd->user,pcmd->argv2,sha1Num);
        mysql_query(conn,sql);
        SQL_LOG(conn);
        res = mysql_store_result(conn);
        if(mysql_num_rows(res) == 0){
            // 2.2.1 发"o"+"o"文件不存在,秒传,插表
            successSend(netfd);
            successSend(netfd);
            // 插表
            insertFileTable(conn,pcmd,sha1Num);
            SQL_LOG(conn);
        }
        else if(mysql_num_rows(res) > 0){
            // 2.2.2 发"e"+msg文件已存在
            errorSend(netfd,NULL);
            errorSend(netfd,"文件已存在!");
        }
        else {
            //报错
            SQL_LOG(conn);
            return -1;
        }
    }
    else {
        // 报错
        SQL_LOG(conn);
        return -1;
    }
    mysql_free_result(res);
    return 0;
}

